/*
Copyright (C) 2007, 2010 - Bit-Blot

This file is part of Aquaria.

Aquaria is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "BitmapFont.h"
#include "Core.h"
#include "tinyxml2.h"
using namespace tinyxml2;
//#include "DSQ.h"

class GLFontLoader
{
public:
	GLFontLoader(glfont::GLFont* font, unsigned int tex)
	{
		this->font = font;
		this->tex = tex;
	}

	virtual bool Load(const char* fontFile) = 0; // Must be implemented by derived class

	glfont::GLFont* font;
	unsigned int tex;
};

class GLFontLoaderXML : public GLFontLoader
{
public:
	GLFontLoaderXML(glfont::GLFont* font, unsigned int tex);

	bool Load(const char* fontFile);
};

GLFontLoaderXML::GLFontLoaderXML(glfont::GLFont* font, unsigned int tex) : GLFontLoader(font, tex)
{

}

bool GLFontLoaderXML::Load(const char *fontFile)
{
	XMLDocument doc;
	XMLError err = readXML(fontFile, doc);
	if (err == XML_ERROR_EMPTY_DOCUMENT)
		return false;

	if (err != XML_SUCCESS)
	{
		errorLog("Failed to load font_small.xml");
		return false;
	}

	unsigned char encoding = 0;
	int textureWidth = 0;
	int textureHeight = 0;
	int ascender = 0;
	int descender = 0;
	int fontoff = 0;
	short fontHeight = 0;
	XMLElement* fontXML = doc.FirstChildElement("font");
	if (fontXML)
	{
		std::string text = fontXML->Attribute("encoding");
		if (text == "utf-8")
			encoding = UTF8;
		else if (text == "utf-16")
			encoding = UTF16;
		else
			encoding = NONE;
		fontHeight = fontXML->IntAttribute("height");
		ascender = fontXML->IntAttribute("ascender");
		descender = fontXML->IntAttribute("descender");
		fontoff = (fontHeight - ascender + descender) / 2;
	}
	XMLElement* textureXML = doc.FirstChildElement("texture");
	if (textureXML)
	{
		textureWidth = textureXML->IntAttribute("width");
		textureHeight = textureXML->IntAttribute("height");
	}

	if (!font->Create(tex, textureWidth, textureHeight, fontHeight, encoding))
		return false;

	XMLElement* charsXML = doc.FirstChildElement("chars");
	if (charsXML)
	{
		XMLElement* charXML = 0;
		charXML = charsXML->FirstChildElement();
		
		while (charXML)
		{
			const char*idText = charXML->Attribute("id");
			int id = font->GetTextChar(idText, 0);
			int advance = charXML->IntAttribute("advance");
			int rect_w = charXML->IntAttribute("rect_w");
			int rect_h = charXML->IntAttribute("rect_h");
			int rect_x = charXML->IntAttribute("rect_x");
			int rect_y = charXML->IntAttribute("rect_y");
			int offset_x = charXML->IntAttribute("offset_x");
			int offset_y = charXML->IntAttribute("offset_y");
			glfont::GLFont::GLFontChar fontChar;
			fontChar.ox = offset_x;
			fontChar.oy = ascender - offset_y + fontoff;
			fontChar.adv = advance;
			fontChar.tx1 = (float)rect_x / (float)textureWidth;
			fontChar.ty1 = (float)rect_y / (float)textureHeight;
			fontChar.tdx = rect_w;
			fontChar.tdy = rect_h;
			font->AddChar(id, fontChar);
			
			charXML = charXML->NextSiblingElement();
		}
	}
	return true;
}

using namespace glfont;

BmpFont::BmpFont()
{
	scale = 1;
	loaded = false;
	overrideTexture = 0;
	fontTopColor = Vector(1,1,1);
	fontBtmColor = Vector(1,1,1);
	offy = 0;
}

BmpFont::~BmpFont()
{
	destroy();
}

void BmpFont::destroy()
{
	if (loaded)
	{
		font.Destroy();
		loaded = false;
	}

	overrideTexture = NULL;
}

void BmpFont::load(const std::string &file, float scale, bool loadTexture)
{
	if (loaded)
		font.Destroy();

	this->scale = scale;

#ifdef BBGE_BUILD_OPENGL
	GLuint id=0;
	glGenTextures(1, &id);

	GLFontLoaderXML loader(&font, id);

	if (!loader.Load(file.c_str()))
		return;
#endif

	loaded = true;
}

Texture *fontTextureTest = 0;
BitmapText::BitmapText(BmpFont *bmpFont)
{
	this->bmpFont = bmpFont;
	bfePass =0;
	bfe = BFE_NONE;

	currentScrollLine = currentScrollChar = 0;
	scrollDelay = 0;
	scrolling = false;
	align = ALIGN_CENTER;
	textWidth = 600;
	this->fontDrawSize = 24;
	//color = Vector(0.5,0.5,1);
	cull = false;
	//setTexture(font);

	alignWidth = 0;

	//fontTextureTest = core->addTexture("font");
}

void BitmapText::autoKern()
{
}

void BitmapText::loadSpacingMap(const std::string &file)
{
	spacingMap.clear();
	InStream inFile(file.c_str());
	std::string line;
	while (std::getline(inFile, line))
	{
		if (!line.empty())
		{
			char c = line[0];
			line = line.substr(2, line.length());
			std::istringstream is(line);
			is >> spacingMap[c];
		}
	}
}

int BitmapText::getWidthOnScreen()
{
	return text.size()*(fontDrawSize/2);
}

void BitmapText::setAlign(Align align)
{
	this->align = align;
}

std::string BitmapText::getText()
{
	return this->text;
}

void BitmapText::setText(const std::string &text)
{
	this->text = text;
	formatText();
}

int BitmapText::findLine(const std::string& label)
{
	for (int i = 0; i < lines.size(); i++)
	{
		if (lines[i].find(label) != std::string::npos)
		{
			return i;
		}
	}
	return 0;
}

void BitmapText::setWidth(float width)
{
	textWidth = width;
}

float BitmapText::getSetWidth()
{
	return textWidth;
}

float BitmapText::getHeight()
{
	float sz = bmpFont->font.GetHeight() * bmpFont->scale;
	return lines.size()*sz;
}

float BitmapText::getLineHeight()
{
	return bmpFont->font.GetHeight() * bmpFont->scale;
}

void BitmapText::formatText()
{
	const char* text = this->text.c_str();
	lines.clear();
	int count = bmpFont->font.GetTextLength(text);

	float currWidth = 0, wordWidth;
	int lineS = 0, lineE = 0, wordS = 0, wordE = 0;
	int wordCount = 0;

	const char* s = " ";
	float spaceWidth = bmpFont->font.GetTextWidth(s, 1);
	bool softBreak = false;

	for (; lineS < count;)
	{
		// Determine the extent of the line
		for (;;)
		{
			// Determine the number of characters in the word
			while (wordE < count &&
				bmpFont->font.GetTextChar(text, wordE) != ' ' &&
				bmpFont->font.GetTextChar(text, wordE) != '\n')
				// Advance the cursor to the next character
				bmpFont->font.GetTextChar(text, wordE, &wordE);

			// Determine the width of the word
			if (wordE > wordS)
			{
				wordCount++;
				wordWidth = bmpFont->font.GetTextWidth(&text[wordS], wordE - wordS);
			}
			else
				wordWidth = 0;

			// Does the word fit on the line? The first word is always accepted.
			if (wordCount == 1 || currWidth + (wordCount > 1 ? spaceWidth : 0) + wordWidth <= textWidth)
			{
				// Increase the line extent to the end of the word
				lineE = wordE;
				currWidth += (wordCount > 1 ? spaceWidth : 0) + wordWidth;

				// Did we reach the end of the line?
				if (wordE == count || bmpFont->font.GetTextChar(text, wordE) == '\n')
				{
					softBreak = false;

					// Skip the newline character
					if (wordE < count)
						// Advance the cursor to the next character
						bmpFont->font.GetTextChar(text, wordE, &wordE);

					break;
				}

				// Skip the trailing space
				if (wordE < count && bmpFont->font.GetTextChar(text, wordE) == ' ')
					// Advance the cursor to the next character
					bmpFont->font.GetTextChar(text, wordE, &wordE);

				// Move to next word
				wordS = wordE;
			}
			else
			{
				softBreak = true;

				// Skip the trailing space
				if (wordE < count && bmpFont->font.GetTextChar(text, wordE) == ' ')
					// Advance the cursor to the next character
					bmpFont->font.GetTextChar(text, wordE, &wordE);

				break;
			}
		}

		// Write the line
		{
			float spacing = 0;
			if (softBreak)
			{
				if (wordCount > 2)
					spacing = (textWidth - currWidth) / (wordCount - 2);
				else
					spacing = (textWidth - currWidth);
			}
			std::string line(&text[lineS], lineE - lineS);
			lines.push_back(line);
		}

		if (softBreak)
		{
			// Skip the trailing space
			if (lineE < count && bmpFont->font.GetTextChar(text, lineE) == ' ')
				// Advance the cursor to the next character
				bmpFont->font.GetTextChar(text, lineE, &lineE);

			// We've already counted the first word on the next line
			currWidth = wordWidth;
			wordCount = 1;
		}
		else
		{
			// Skip the line break
			if (lineE < count && bmpFont->font.GetTextChar(text, lineE) == '\n')
				// Advance the cursor to the next character
				bmpFont->font.GetTextChar(text, lineE, &lineE);

			currWidth = 0;
			wordCount = 0;
		}

		// Move to next line
		lineS = lineE;
		wordS = wordE;
	}
	colorIndices.clear();
}

void BitmapText::setBitmapFontEffect(BitmapFontEffect bfe)
{
	this->bfe = bfe;
}

void BitmapText::updateWordColoring()
{
	colorIndices.resize(lines.size());
	for (int i = 0; i < colorIndices.size(); i++)
	{
		colorIndices[i].resize(lines[i].size());
		for (int j = 0; j < colorIndices[i].size(); j++)
		{
			colorIndices[i][j] = Vector(1,1,1);
		}
	}

	/*
	for (int i = 0; i < lines.size(); i++)
	{
		int c = 0;
		for (int t = 0; t < dsq->continuity.wordColoring.size(); t++)
		{
			WordColoring *w = &dsq->continuity.wordColoring[t];
			if ((c = lines[i].find(w->word)) != std::string::npos)
			{
				for (int j = c; j < c + w->word.size(); j++)
				{
					colorIndices[i][j] = w->color;
				}
			}
		}
	}
	*/
	//lines.push_back(text);

}

bool BitmapText::isEmpty()
{
	return lines.empty();
}

void BitmapText::scrollText(const std::string &text, float scrollSpeed)
{
	if (text.empty()) return;
	this->scrollSpeed = scrollSpeed;
	scrollDelay = scrollSpeed;
	this->text = text;
	scrolling = true;
	formatText();
	currentScrollLine = 0;
	currentScrollChar = 0;
}

void BitmapText::setFontSize(float sz)
{
	this->fontDrawSize = sz;
}

void BitmapText::onUpdate(float dt)
{
	bfePass = 0;
	RenderObject::onUpdate(dt);
	if (scrollDelay > 0 && scrolling)
	{
		if (lines.empty())
		{
			currentScrollLine = 0;
			scrolling = false;
			scrollDelay = 0;
		}
		else
		{
			scrollDelay -= dt;
			while (scrollDelay <= 0)
			{
				float diff = scrollDelay;
				scrollDelay = scrollSpeed;
				scrollDelay += diff;
				currentScrollChar ++;
				if (currentScrollChar >= lines[currentScrollLine].size())
				{
					currentScrollLine++;
					currentScrollChar = 0;
					if (currentScrollLine >= lines.size())
					{
						currentScrollLine = 0;
						scrolling = false;
					}
				}
			}
		}
	}
}

Vector BitmapText::getColorIndex(int i, int j)
{
	Vector c(1,1,1);
	if (!(i < 0 || j < 0))
	{
		if ( i < colorIndices.size() && j < colorIndices[i].size())
		{
			c = colorIndices[i][j];
		}
	}
	return c;
}

void BitmapText::onRender()
{
	if (!bmpFont) return;
	float top_color[3] = {bmpFont->fontTopColor.x*color.x, bmpFont->fontTopColor.y*color.y, bmpFont->fontTopColor.z*color.z};
	float bottom_color[3] = {bmpFont->fontBtmColor.x*color.x, bmpFont->fontBtmColor.y*color.y, bmpFont->fontBtmColor.z*color.z};

#ifdef BBGE_BUILD_OPENGL
	glEnable(GL_TEXTURE_2D);
	/*
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	*/
	//glDisable(GL_CULL_FACE);

	//glScalef(1, -1, 0);

	bmpFont->font.Begin();

	if (fontTextureTest) fontTextureTest->apply();

	if (bmpFont->overrideTexture) bmpFont->overrideTexture->apply();

	float y= bmpFont->offy;
	float x=0;

	float adj = bmpFont->font.GetHeight() * bmpFont->scale;
	int len = 0;

	if (scrolling)
	{
		for (int i = 0; i <= currentScrollLine; i++)
		{
			std::string theLine = lines[i];
			if (i == currentScrollLine)
				theLine = theLine.substr(0, currentScrollChar);

			x=0;
			if (align == ALIGN_CENTER)
			{
				float w = bmpFont->font.GetTextWidth(theLine.c_str(), len);
				x = - w * 0.5f * bmpFont->scale;
			}
			float la = 1.0f-(scrollDelay/scrollSpeed);
			/*
			std::ostringstream os;
			os << "la: " << la;
			debugLog(os.str());
			*/

			bmpFont->font.DrawString(theLine.c_str(), bmpFont->scale, x, y, top_color, bottom_color, alpha.x, la);
			y += adj;
		}
	}
	else
	{
		for (int i = 0; i < lines.size(); i++)
		{
			x = 0;
			if (align == ALIGN_CENTER)
			{
				float w = bmpFont->font.GetTextWidth(lines[i].c_str(), len);
				x = -w * 0.5f * bmpFont->scale;
			}
			else 
			{
				x = 0;
			}
			bmpFont->font.DrawString(lines[i].c_str(), bmpFont->scale, x, y, top_color, bottom_color, alpha.x, 1);
			y += adj;
		}
	}
	
	//glEnable(GL_CULL_FACE);
	glBindTexture(GL_TEXTURE_2D, 0);

#endif
}

void BitmapText::unloadDevice()
{
	RenderObject::unloadDevice();
}

void BitmapText::reloadDevice()
{
	RenderObject::reloadDevice();
	setText(this->text);
}

void BitmapText::render()
{
	RenderObject::render();
	if (!bfePass)
	{
		if (bfe == BFE_SHADOWBLUR)
		{
			InterpolatedVector oldAlpha = alpha, oldPos = position;
			Vector adjust(rand()%5-2, rand()%5-2+2);
			position += adjust;
			alpha *= 0.4f;
			bfePass = 1;
			RenderObject::render();
			alpha = oldAlpha;
			position = oldPos;
		}
	}
}

void BitmapText::stopScrollingText()
{
	scrolling = false;
}

bool BitmapText::isScrollingText()
{
	return scrolling;
}

int BitmapText::getNumLines()
{
	return lines.size();
}

float BitmapText::getStringWidth(const std::string& text)
{
	return bmpFont->font.GetTextWidth(text.c_str(), 0) * bmpFont->scale;
}

/*
BitmapText::BitmapText() : RenderObject()
{
	cull = false;
	followCamera = 1;
	scrollSpeed = 0.1f;
}

void BitmapText::scrollText(const std::string &text, float scrollSpeed)
{
	setText(text);
	this->scrollSpeed = scrollSpeed;
}

void BitmapText::setText(const std::string &text)
{
	this->text = text;
}

std::string BitmapText::getText()
{
	return text;
}

void BitmapText::onRender()
{
	CTextDrawer::GetSingleton().SetColor(color.x, color.y, color.z, alpha.getValue());
	dsq->print(position.x, 600 - (position.y + 16*2), text);
}
*/
